package com.soo.hoo.core.handler;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * <p>
 * 加解密handler
 * </p>
 * <pre>
 * 配合MybatisPlus使用，需要修改{@code @TableName}注解的属性autoResultMap值为true（如果不设置为true，MybatisPlus的QueryWrapper不会进入setNonNullParameter方法），然后在需要加解密的字段上添加注解{@code @TableField}，设置注解属性typeHandler为{@link SecureTypeHandler}类
 * </pre>
 *
 * @author 杨燚
 * @since 2022/11/29
 */
@MappedJdbcTypes(JdbcType.VARCHAR)
public class SecureTypeHandler extends BaseTypeHandler<String> {

    /**
     * 加解密的key，如果系统已经有数据，请不要修改，不然会影响原数据的解密；
     * 且有可能导致服务无法启动（可能配置数据初始化等工作，而初始化工作中可能用到解密）
     */
    private static final byte[] KEY = SecureUtil.generateKey(
            SymmetricAlgorithm.AES.getValue(), "7ba0f44025684883b27e676d131a7863".getBytes()
    ).getEncoded();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, encrypt(parameter));
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return decrypt(rs.getString(columnName));
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return decrypt(rs.getString(columnIndex));
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return decrypt(cs.getString(columnIndex));
    }

    /**
     * 解密
     *
     * @param value 待解密内容
     * @return 解密后的内容
     */
    private String decrypt(String value) {
        if (StrUtil.isBlank(value)) {
            return value;
        }
        return SecureUtil.aes(KEY).decryptStr(value);
    }

    /**
     * 加密
     *
     * @param value 待加密的内容
     * @return 十六进制的字符
     */
    private String encrypt(String value) {
        if (StrUtil.isBlank(value)) {
            return null;
        }
        return SecureUtil.aes(KEY).encryptHex(value);
    }

}
